#include <iostream>
#include <sys/ipc.h>
#include <sys/shm.h>
#include <string.h>
#include <stdio.h>
#include <unistd.h>

#define CREATER "creater"
#define USER "user"
#define ERR_EXIT(m)         \
    do                      \
    {                       \
        perror(m);          \
        exit(EXIT_FAILURE); \
    } while (0)

const int gshm_id = -1;
const int gsize = 4096;
const std::string gpath = ".";
const int gproj_id = 1000;
const int gmode = 0666;

class Shm
{
private:
    void CreateHelper(int mode)
    {

        //printf("CreateHelper");

        _shmid = shmget(_key, _size, mode);
        if (_shmid < 0)
        {
            ERR_EXIT("shmget");
        }
        printf("shmget: %d\n", _shmid); // 打印shmget
    }

    // 申请共享内存
    void Create()
    {
        CreateHelper(IPC_CREAT | IPC_EXCL | gmode);
    }

    // 打开已有共享内存
    void Get()
    {
        CreateHelper(IPC_CREAT);
    }

    // 关联共享内存
    void Attach()
    {
        _start_mem = shmat(_shmid, nullptr, 0);
        if ((long long)_start_mem < 0)
        {
            ERR_EXIT("shmat");
        }
        else
        {
            printf("attach success\n"); // 打印shmat
        }
    }

    //去关联共享内存
    void Detach()
    {
        int n = shmdt(_start_mem);
        if (n == 0)
        {
            printf("detach success\n"); 
        }
    }

    // 删除共享内存
    void Destroy()
    {
        //去关联
        Detach();

        if (_shmid == gshm_id)
            return;

        int n = shmctl(_shmid, IPC_RMID, nullptr);
        if (n == 0)
        {
            printf("shared_memory destroy success, shmid: %d\n", _shmid); // 打印shmctl
        }
        else
        {
            ERR_EXIT("shmctl");
        }
    }

public:
    // 构造函数
    Shm(const std::string &pathname, const int projid, const std::string &usertype) : _pathname(pathname),
                                                                                      _size(gsize),
                                                                                      _shmid(gshm_id),
                                                                                      _start_mem(nullptr),
                                                                                      _usertype(usertype)
    {
        // 获取key
        _key = ftok(_pathname.c_str(), projid);
        if (_key == -1)
        {
            ERR_EXIT("ftok");
        }
        printf("key: %d\n", _key); // 打印key

        // 根据用户类型，创建/打开 共享内存
        if (_usertype == CREATER)
        {
            Create();
        }
        else if (_usertype == USER)
        {
            Get();
        }
        else
        {
        }

        // 关联共享内存
        Attach();
    }

    // 获取共享内存大小
    int Size()
    {
        return _size;
    }

    // 获取共享内存开头地址
    void *VirtualAdd()
    {
        printf("start_mem: %p\n", _start_mem); // 打印共享内存开头地址
        return _start_mem;
    }

    // 析构函数
    ~Shm()
    {
        if (_usertype == CREATER)
        {
            // 删除共享内存
            Destroy();
        }
    }

private:
    std::string _pathname;
    int _shmid;
    int _size;
    void *_start_mem;
    key_t _key;
    std::string _usertype;
};